package com.freesun.shop.service.imp;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.freesun.shop.service.SysMenuService;
import com.freesun.shop.mapper.SysRoleMenuMapper;
import com.freesun.shop.mapper.SysUserRoleMapper;
import com.freesun.shop.vo.Auth;
import com.freesun.shop.vo.Menu;
import com.freesun.shop.vo.MenuAuths;
import com.freesun.shop.entity.SysMenu;
import com.freesun.shop.mapper.SysMenuMapper;
import com.freesun.shop.entity.SysRoleMenu;
import com.freesun.shop.entity.SysUserRole;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 菜单管理 服务实现类
 * </p>
 *
 * @author zf
 * @since 2019-11-13
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {
    private static Logger log = LoggerFactory.getLogger(SysMenuServiceImpl.class);



    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;
    @Autowired
    private SysRoleMenuMapper sysRoleMenuMapper;
    @Autowired
    private SysMenuMapper sysMenuMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Cacheable(cacheNames = "com.freesun.shop.vo.MenuAuths",key = "#userId")
    @Override
    public MenuAuths loadMenuAndAuths(Long userId) {
        log.info("加载用户:{}的菜单数据",userId);
        MenuAuths menuAuths=new MenuAuths();

        //根据ID获取所有的菜单列表
        List<SysMenu> menus = loadUserAllMenuNoTree(userId);
        List<Menu> menuList=getTreeMenus(menus);
        List<Auth> authorutes=getAuths(menus);
        //把用户的权限信息放入redis里面
        List<String> permission =new ArrayList<String>(authorutes.size());
        authorutes.forEach(auth -> permission.add(auth.getAuthority()));
        //权限存入redis 里面
        redisTemplate.opsForValue().set(Auth.USER_AUTH_PREFIX+userId, JSONUtil.toJsonStr(permission));
        menuAuths.setMenuList(menuList);
        menuAuths.setAuthorities(authorutes);
        return menuAuths;
    }

    /**
     * 加载权限信息
     * @param menus
     * @return
     */
    private List<Auth> getAuths(List<SysMenu> menus) {
        if(null==menus||menus.isEmpty()){
            return null;
        }
        List<Auth> auths = new ArrayList<>();
        menus.forEach((menu)->{
            String perms = menu.getPerms();
            if(StringUtils.hasText(perms)){
                String[] split = perms.split("\\,");
                for (String s : split) {
                    auths.add(Auth.of(s));
                }

            }
        });
        return auths;
    }

    private List<Menu> getTreeMenus(List<SysMenu> menus) {
        if(null==menus||menus.isEmpty()){
            return null;
        }
        ArrayList<Menu> rootMenus = new ArrayList<>();
        menus.forEach((sysMenu) -> {
            if(sysMenu.getParentId().equals(0L)){
               Menu menu = sysMenuTreeMenu(sysMenu);
                rootMenus.add(menu);
            }
        });
        menus.forEach((sysMenu) -> {
            rootMenus.forEach((menu1) -> {
                if(menu1.getMenuId().equals(sysMenu.getParentId())){
                    Menu menuSec=sysMenuTreeMenu(sysMenu);
                    menu1.getList().add(menuSec);
                }
            });
        });
        return rootMenus;
    }

    private Menu sysMenuTreeMenu(SysMenu sysMenu) {
        Menu menu = new Menu(sysMenu.getName(),sysMenu.getUrl(), sysMenu.getMenuId());
        return menu;
    }

    /**
     * userid-->roleid-->MenuId-->Menu
     * @param userId
     * @return
     */
    private List<SysMenu> loadUserAllMenuNoTree(Long userId) {
        List<Object> userRoleIds = sysUserRoleMapper.selectObjs(
                new LambdaQueryWrapper<SysUserRole>()
                        .select(SysUserRole::getRoleId)
                        .eq(SysUserRole::getUserId, userId)
        );
        if(null==userRoleIds||userRoleIds.isEmpty()){
            return null;
        }
        List<Object> menuId = sysRoleMenuMapper.selectObjs(new LambdaQueryWrapper<SysRoleMenu>()
                .select(SysRoleMenu::getMenuId).in(SysRoleMenu::getRoleId, userRoleIds)
        );
        if(null==menuId||menuId.isEmpty()){
            return null;
        }
        List<SysMenu> menus = sysMenuMapper.selectList(new LambdaQueryWrapper<SysMenu>()
                .in(SysMenu::getMenuId, menuId).orderByAsc(SysMenu::getOrderNum));
        return menus;
    }

    @Override
    @Transactional
    public boolean removeById(Serializable id) {
        Assert.notNull(id,"删除的ID不能为Null");
        log.info("本次删除的菜单ID为:{}",id);
        Integer subNodeCount = sysMenuMapper.selectCount(new LambdaQueryWrapper<SysMenu>().eq(SysMenu::getParentId, id));
        if(null!=subNodeCount&&subNodeCount>0){
            throw new RuntimeException("删除的菜单包含子节点");
        }
        Integer roleMenuNum = sysRoleMenuMapper.selectCount(new LambdaQueryWrapper<SysRoleMenu>()
                .select(SysRoleMenu::getRoleId)
                .eq(SysRoleMenu::getRoleId, id));
        if (null != roleMenuNum && roleMenuNum>0) {
            throw new RuntimeException("该菜单有对应的角色,删除角色和菜单的关系后,才能删除");
        }
        boolean flag = super.removeById(id);
        return flag;
    }

    @Override
    public List<SysMenu> listMenuNoButtn() {
        List<SysMenu> sysMenus = sysMenuMapper.selectList(new LambdaQueryWrapper<SysMenu>()
                .ne(SysMenu::getType, 2));
        return sysMenus;
    }

    /**
     * 新增菜单
     * @param entity
     * @return
     */
    @Override
    public boolean save(SysMenu entity) {
        Assert.notNull(entity,"要新增的菜单不能问Null");
        Assert.notNull(entity.getType(),"要新增的菜单类型不能问Null");
        log.info("新增的菜单类型为:{},名称为:{}",entity.getType(),entity.getName());

        checkMenu(entity);
        boolean flag = super.save(entity);
        return flag;
    }

    private void checkMenu(SysMenu entity) {
        switch (entity.getType()){
            case 0:
                if(entity.getParentId()!=0|| StringUtils.hasText(entity.getUrl())||StringUtils.hasText(entity.getPerms())){
                    throw new RuntimeException("新增目录时,参数不符合要求");
                }
                break;
            case 1:
                if(entity.getParentId()==null||entity.getParentId()==0||StringUtils.hasText(entity.getPerms())||!StringUtils.hasText(entity.getUrl())){
                    throw new RuntimeException("新增菜单时,参数不符合要求");
                }
                break;
            case 2:
                if(entity.getParentId()==null||entity.getParentId()==0||!StringUtils.hasText(entity.getPerms())){
                    throw new RuntimeException("新增按钮时,参数不符合要求");
                }
                break;
            default:
                throw new RuntimeException("不支持的菜单类型。");
        }
    }

    /**
     * 修改菜单数据
     * @param entity
     * @return
     */
    @Override
    public boolean updateById(SysMenu entity) {
        Assert.notNull(entity,"要修改的菜单不能问Null");
        Assert.notNull(entity.getMenuId(),"要修改的菜单Id不能问Null");
        Assert.notNull(entity.getType(),"要修改的菜单类型不能问Null");
        log.info("修改的菜单id为:{},类型为:{},名称为:{}",entity.getMenuId(),entity.getType(),entity.getName());
        SysMenu sysMenuDb = getById(entity.getMenuId());
        if (null == sysMenuDb) {
            throw new RuntimeException("要修改的数据不存在");
        }
        if(sysMenuDb.getType()!=entity.getType()){
            throw new RuntimeException("修改的菜单类型不一致");
        }
        //校验修改的数据格式匹配
        checkMenu(entity);
        return super.updateById(entity);
    }
}
